package com.gid.rcflightlogger;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import kankan.wheel.R;
import kankan.wheel.widget.WheelView;
import kankan.wheel.widget.adapters.NumericWheelAdapter;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Vibrator;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;

import com.gid.rcflightlogger.R.drawable;
import com.orm.androrm.Filter;

// Provides a countdown timer, and an diaglog option at the end of the countdown to log flight to db
//
// Some "state machine" mechanics
//	- aircraft and battery selectors
//		. aircraft shows full list
//		. battery selector 1 full list
//		. battery selector 2 empty untill 1 is selected, updated when 1's selection is changed
//
//	- buttons (start and reset)
//		. start = starts the count down, changed to stop, also disables the spinners
//		. stop = stops the countdown
//		. reset = if count down already started, resets timer to full value and stop the count down 
public class FlightTimerActivity extends BaseActivity {
	
	private static final int APP_VERSION = 6; //TODO: need to hard code this number each release
	private static final int DEFAULT_TIMER_SECS	= 6*60;
	private static final int TIMER_DELAY_SECS = 5;
	
	public static final String PREF_UPTIMER = "UpTimer";
	private static final String PREF_BEEP = "Beep";
	private static final String PREF_VIBRATE = "Vibrate";
	private static final String PREF_TIMER_PREFIX = "TimerVal";
	
	private static final int DIALOG_ID_AUTOLOG = 0;
	private static final int DIALOG_ID_MANUALLOG = 1;
	private static final int DIALOG_ID_BATTERYSELECT = 5;
	
	private static final int TIMER_MAX_MINUTES = 30;
	private static final int TIMER_TEXT_SIZE = 36;
	
	//db objects
	private Aircraft mEmptyAircraft;
	private List<Aircraft> mAircrafts;	//all aircrafts
	private List<Battery> mBatteries; //all batteries
	
	//selection details
	Aircraft mSelectedAircraft = null;
	private Map<Integer, Battery> mSelectedBatteries; //selected batteries, indexed by position in list
	
	//list for spinners
	private List<Aircraft> mAircraftSpinnerList;
	
	//adapters for spinners/lists
	private ArrayAdapter<Aircraft> mAircraftSpinnerAdapter;
	private BatterySelectListAdapter mBatteryListAdapter;

	//form objects
	private Spinner mAircraftSpinner;
	private TextView mBatterySelector;
	
	WheelView mTimePickerMins;
	WheelView mTimePickerSecs;
	ImageView mDirection;
	
	private CheckBox mBeep;
	private CheckBox mVibrate;
	private CheckBox mUptimer;
	
	private Button mStartStop;
	private Button mPause;
	private Button mLog;
	
    //timer service
    LocalTimerService mTimerService;
    
    //timer state
	public enum TimerState
	{
		OFF,
		COUNTING_DOWN,
		COUNTING_DOWN_PAUSED,
		COUNTING_UP,
		COUNTING_UP_PAUSED
	};
	
	TimerState mTimerState = TimerState.OFF;
	private int mTimerCountDownInterval = DEFAULT_TIMER_SECS;	//updated on start timer count
	private int mTimerInitCounter = 0;
	private int mTimerMainCounter = 0;
	
	//used for logging
	private int mFlightLogTime = 0;
	
	//alert stuff
	private enum AlertType
	{
		TICK,
		SOON,
		END
	};
	
	private MediaPlayer mBeepPlayer = null;
	private int mBeepCounter = 0;
	
	private boolean dbloaded = false;
		
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.layout_flighttimer);
		
		mCurrentMenuID = R.id.menu_main_flighttimer;
			    	
		mEmptyAircraft = new Aircraft();
		mEmptyAircraft.setName("");
		mAircrafts = new ArrayList<Aircraft>();
		
		mBatteries = new ArrayList<Battery>();
		mSelectedBatteries = new HashMap<Integer, Battery>();
		
		//make spinner lists
		mAircraftSpinnerList = new ArrayList<Aircraft>();

		//prepare adapters 
		mAircraftSpinnerAdapter = new ArrayAdapter<Aircraft>(this, android.R.layout.simple_spinner_item, mAircraftSpinnerList);
		mBatteryListAdapter = new BatterySelectListAdapter(this, mBatteries);

		//get UI elements
		mAircraftSpinner = (Spinner) findViewById(R.id.flighttimer_spinner_aircraft);
		mAircraftSpinner.setAdapter(mAircraftSpinnerAdapter);
		
		mBatterySelector = (TextView) findViewById(R.id.flighttimer_battery_selector);
				
		mTimePickerMins = (WheelView) findViewById(R.id.flighttimer_timepicker_mins);
		TimeNumericAdapter minsadapter = new TimeNumericAdapter(this, 0, TIMER_MAX_MINUTES, "%02d");
		minsadapter.setTextSize(TIMER_TEXT_SIZE);
		mTimePickerMins.setViewAdapter(minsadapter);
	
		mTimePickerSecs = (WheelView) findViewById(R.id.flighttimer_timepicker_secs);
		TimeNumericAdapter secssadapter = new TimeNumericAdapter(this, 0, 59, "%02d");
		secssadapter.setTextSize(TIMER_TEXT_SIZE);
		mTimePickerSecs.setViewAdapter(secssadapter);
		mTimePickerSecs.setCyclic(true);
		
		setTimePickerValue(DEFAULT_TIMER_SECS, mTimePickerMins, mTimePickerSecs);
		
		mDirection = (ImageView) findViewById(R.id.flighttimer_timepicker_direction);
		
		mBeep = (CheckBox) findViewById(R.id.flighttimer_chbx_beep);
		mBeep.setChecked(mPrefs.getBoolean(PREF_BEEP, true));
		
		mVibrate = (CheckBox) findViewById(R.id.flighttimer_chbx_vibrates);
		mVibrate.setChecked(mPrefs.getBoolean(PREF_VIBRATE, true));
		
		mUptimer = (CheckBox) findViewById(R.id.flighttimer_chbx_uptimer);
		mUptimer.setChecked(mPrefs.getBoolean(PREF_UPTIMER, false));
		
		mStartStop = (Button) findViewById(R.id.flighttimer_btn_start);
		mPause = (Button) findViewById(R.id.flighttimer_btn_pause);
		mLog = (Button) findViewById(R.id.flighttimer_btn_log);
		
		//set up listeners for spinners
		mAircraftSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){

			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				if (mAircraftSpinner.getSelectedItemPosition() != 0)
					mSelectedAircraft = (Aircraft) mAircraftSpinner.getSelectedItem();
				else
					mSelectedAircraft = null;
				
				loadTimerPrefValue();
				updateUI();
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				mSelectedAircraft = null;
				loadTimerPrefValue();
				updateUI();
			}
			
		});
		
		//listener for battery selector
		mBatterySelector.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				//myUtils.makeToast(FlightTimerActivity.this, "Clicked");
				showDialog(DIALOG_ID_BATTERYSELECT);
			}
		});
		
		//set up listeners for buttons
		mStartStop.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				if (mTimerService == null)
					return;
				if (mTimerState == TimerState.OFF)
				{
					//start timer, initializing counters and various other stuff
					mTimerCountDownInterval = getTimePickerValue(mTimePickerMins, mTimePickerSecs);
					mTimerInitCounter = TIMER_DELAY_SECS;
					mTimerMainCounter = mTimerCountDownInterval;
					
					//save prefs
					mPrefsEditor.putBoolean(PREF_BEEP, mBeep.isChecked());
					mPrefsEditor.putBoolean(PREF_VIBRATE, mVibrate.isChecked());
					mPrefsEditor.putBoolean(PREF_UPTIMER, mUptimer.isChecked());
					mPrefsEditor.putInt(buildTimerPrefKey(), mTimerCountDownInterval);
					mPrefsEditor.commit();
					
					//start the timer
					mTimerState = TimerState.COUNTING_DOWN;
					mTimerService.startTimer();
					myUtils.makeToast(FlightTimerActivity.this, "Timer will start counting down in " + TIMER_DELAY_SECS + " seconds.");
				}
				else
				{
					mTimerService.stopTimer();
					if (mTimerState == TimerState.COUNTING_DOWN || mTimerState == TimerState.COUNTING_DOWN_PAUSED)
						mFlightLogTime = Math.max(mTimerCountDownInterval - mTimerMainCounter, 0); //timer stopped while counting down, log flight duration only
					else if (mTimerState == TimerState.COUNTING_UP ||  mTimerState == TimerState.COUNTING_UP_PAUSED)
						mFlightLogTime = mTimerCountDownInterval + mTimerMainCounter; //timer stopped while counting up, flight duration is combination of down and up time

					//if no asset is selected for logging, flight log cannot be done
					if (mSelectedAircraft != null || !mSelectedBatteries.isEmpty())
						showDialog(DIALOG_ID_AUTOLOG);
					
					mTimerState = TimerState.OFF;
					setTimePickerValue(mTimerCountDownInterval, mTimePickerMins, mTimePickerSecs); //reset timer display (also do the same at end of countdown)
				}
				
				updateUI();
			}
			
		});
		
		mPause.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {			
				switch (mTimerState)
				{
				case COUNTING_DOWN:
				case COUNTING_UP:
					mTimerState = (mTimerState == TimerState.COUNTING_DOWN ? TimerState.COUNTING_DOWN_PAUSED : TimerState.COUNTING_UP_PAUSED);
					if (mTimerService != null)
						mTimerService.pauseTimer();
					break;
				case COUNTING_DOWN_PAUSED:
				case COUNTING_UP_PAUSED:
					mTimerState = (mTimerState == TimerState.COUNTING_DOWN_PAUSED ? TimerState.COUNTING_DOWN: TimerState.COUNTING_UP);					
					if (mTimerService != null)
						mTimerService.unpauseTimer();
					break;
				}
				updateUI();
			}
		});
		
		mLog.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				//if no asset is selected for logging, flight log cannot be done
				if (mSelectedAircraft == null && mSelectedBatteries.isEmpty())
					return;
				
				mFlightLogTime = getTimePickerValue(mTimePickerMins, mTimePickerSecs);
				mPrefsEditor.putInt(buildTimerPrefKey(), mFlightLogTime);
				mPrefsEditor.commit();
				showDialog(DIALOG_ID_MANUALLOG);
			}
		});
		
	
		//set up beep player
		mBeepPlayer = MediaPlayer.create(this, R.raw.longbeep);
		mBeepPlayer.setOnCompletionListener(new OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer mp) {
				if (mBeepCounter > 0)
					mBeepCounter--;
				
				if (mBeepCounter > 0)
				{
					mBeepPlayer.seekTo(0);
					mBeepPlayer.start();
				}
			}
		});
		
		//finally, bind to timer service
		bindService(new Intent(this, LocalTimerService.class), mConnection, Context.BIND_AUTO_CREATE);
	}
	
	@Override
	protected void onDestroy() {
		mBeepPlayer.stop();
		mBeepPlayer.release();
		mBeepPlayer = null;
		//unbind service
		unbindService(mConnection);
		mTimerService = null;
		super.onDestroy();
	}
	
	@Override
	protected void onResume() {
		super.onResume();
		//load data from db
		loadDBData();
		updateUI();
		
		//show some situational tip dialogs
		final String FIRST_RUN = "FirstRun";
		final String WHATSNEW = "WhatsNew";
		if (mAircrafts.isEmpty() && mBatteries.isEmpty() && mPrefs.getBoolean(FIRST_RUN, true))
		{
			mPrefsEditor.putBoolean(FIRST_RUN, false);
			mPrefsEditor.putInt(WHATSNEW, APP_VERSION); //we dont want to show tips if this is first run
			mPrefsEditor.commit();
			showDialog(DIALOG_ID_USAGE);
		}
		else
		{
			int lastversion = mPrefs.getInt(WHATSNEW, 0);
			if (lastversion < APP_VERSION)
			{
				mPrefsEditor.putInt(WHATSNEW, APP_VERSION);
				mPrefsEditor.commit();
				showDialog(DIALOG_ID_WHATSNEW);
			}
		}
	}
	
	
	//helper functions to create different kind of dialogs
	//1- flight log dialog, expecting id = DIALOG_ID_AUTOLOG or DIALOG_ID_MANUALLOG
	private Dialog createFlightLogDialog(int id)
	{
		if (id != DIALOG_ID_AUTOLOG && id != DIALOG_ID_MANUALLOG)
		{
			return null;
		}
	
		//build dialog
		AlertDialog.Builder builder = new AlertDialog.Builder(FlightTimerActivity.this);
	
		if (id == DIALOG_ID_AUTOLOG)
		{
			builder.setTitle("Log this flight?");
		}
		else
		{
			builder.setTitle("Add flight log?");
		}
		builder.setIcon(drawable.myicon);
		
		//retrieve the layout
		LinearLayout box = (LinearLayout)getLayoutInflater().inflate(R.layout.layout_flightlogdialog, null);
		
		//configure the flight timer wheels
		WheelView minspicker = (WheelView) box.findViewById(R.id.flightlogdialog_timepicker_mins);
		TimeNumericAdapter minsadapter = new TimeNumericAdapter(this, 0, 2*TIMER_MAX_MINUTES, "%02d");
		minspicker.setViewAdapter(minsadapter);
	
		WheelView secspicker = (WheelView) box.findViewById(R.id.flightlogdialog_timepicker_secs);
		TimeNumericAdapter secssadapter = new TimeNumericAdapter(this, 0, 59, "%02d");
		secspicker.setViewAdapter(secssadapter);
		secspicker.setCyclic(true);
		
		//configure the flight outcome check boxes
	    RadioGroup rg = (RadioGroup) box.findViewById(R.id.flightlogdialog_radio_group);  
		for (int i = 0; i < Flight.FLIGHT_OUTCOME_LABELS.length; i++)
		{
			RadioButton btn = new RadioButton(this);
			btn.setId(i);
			btn.setText(Flight.FLIGHT_OUTCOME_LABELS[i]);
			btn.setChecked((i==0));
			rg.addView(btn);
		}
		builder.setView(box);
		builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id2) {
				RadioGroup gr = (RadioGroup) ((Dialog)dialog).findViewById(R.id.flightlogdialog_radio_group);
			
				ArrayList<String> items = new ArrayList<String>(); //array to store list of things we added logs for
				WheelView minspicker = (WheelView) ((Dialog)dialog).findViewById(R.id.flightlogdialog_timepicker_mins);
				WheelView secspicker = (WheelView) ((Dialog)dialog).findViewById(R.id.flightlogdialog_timepicker_secs);
				//get time from the dialog's time picker, as user might have adjusted it
				int logtime = getTimePickerValue(minspicker, secspicker);
				
				if (mSelectedAircraft != null)
				{
					Flight flight = new Flight();
					flight.setAircraft(mSelectedAircraft);
					flight.setDuration(logtime);
					flight.setOutcome(gr.getCheckedRadioButtonId());
					flight.save(FlightTimerActivity.this);
					items.add(mSelectedAircraft.getName());
				}
				
				for (Battery b : mSelectedBatteries.values())
				{
					Cycle cycle = new Cycle();
					cycle.setBattery(b);
					cycle.setDuration(logtime);
					cycle.setCount(1);
					cycle.save(FlightTimerActivity.this);
					items.add(b.getTag());
				}
				
				if (!items.isEmpty())
				{
					String names = "";
					boolean first = true;
					for (String item : items)
					{
						if (!first)
							names += ", ";
						else
							first = false;
						names += item;
					}
					myUtils.makeToast(FlightTimerActivity.this, "Flight log added for: " + names);
				}
			}
		});
		builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				dialog.cancel();
			}
		});
		builder.setCancelable(false);
		return builder.create();
	}
	
	private Dialog createBatterySelectorDialog(int id)
	{
		if (id != DIALOG_ID_BATTERYSELECT)
		{
			return null;
		}
	
		//if no batteries are defined, prompt user
		if (mBatteries.isEmpty())
		{
			if (dbloaded)
				myUtils.makeToast(this, "No batteries available, please add batteries (use Menu - Inventory - Batteries).");
			return null;
		}
		
		//build dialog
		AlertDialog.Builder builder = new AlertDialog.Builder(FlightTimerActivity.this);
		builder.setTitle("Select batteries");
		builder.setIcon(drawable.myicon);
		
		//retrieve the layout
		ListView blist = (ListView)getLayoutInflater().inflate(R.layout.layout_batteryselectordialog, null);
		blist.setAdapter(mBatteryListAdapter);
		blist.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
		builder.setView(blist);
		
		builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				dialog.cancel();
			}
		});
		builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id2) {
				//clear all selections
				ListView blist = (ListView) ((Dialog)dialog).findViewById(R.id.batteryselectordialog_listview);
				SparseBooleanArray posarr = blist.getCheckedItemPositions();
				Log.i(LOG_TAG, "Battery select dialog returns " +  posarr.size() + " items");
				mSelectedBatteries.clear();
				for (int i = 0;  i < posarr.size(); i++)
				{
					int key = posarr.keyAt(i);
					boolean selected = posarr.valueAt(i);
					if (key >= mBatteries.size() || !selected)
					{
						Log.e(LOG_TAG, "Invalid battery selection: index = " +  key + ", state = " + selected);
						continue;
					}
					//if valid, insert into selected list
					mSelectedBatteries.put(key, mBatteries.get(key));
				}
				loadTimerPrefValue();
				updateUI();
			}
		});
		
		builder.setCancelable(false);
		return builder.create();
	}
	
	@Override
	protected Dialog onCreateDialog(int id) {
		if (id == DIALOG_ID_AUTOLOG || id == DIALOG_ID_MANUALLOG)
		{	
			return createFlightLogDialog(id);
		}
		else if (id == DIALOG_ID_BATTERYSELECT)
		{
			return createBatterySelectorDialog(id);
		}
		else
			return super.onCreateDialog(id);
	}
	
	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {
		super.onPrepareDialog(id, dialog);
		if (id == DIALOG_ID_AUTOLOG || id == DIALOG_ID_MANUALLOG)
		{
			WheelView minspicker = (WheelView) dialog.findViewById(R.id.flightlogdialog_timepicker_mins);
			WheelView secspicker = (WheelView) dialog.findViewById(R.id.flightlogdialog_timepicker_secs);
			setTimePickerValue(mFlightLogTime, minspicker, secspicker);
		}
		else if (id == DIALOG_ID_BATTERYSELECT)
		{
			//clear all selections
			ListView blist = (ListView) dialog.findViewById(R.id.batteryselectordialog_listview);
			blist.clearChoices();
			for (Integer i : mSelectedBatteries.keySet()) {
				blist.setItemChecked(i, true);
			}
		}
	}
	
	private void loadDBData()
	{
		Log.i(LOG_TAG, "Reloading DB data");
		
		//load data from db
		Filter filter = new Filter();
		filter.is("mStatus", 1);
		
		//refresh db data
		mAircrafts.clear();
		mAircrafts.addAll(Aircraft.objects(getApplicationContext()).filter(filter).toList());
		mBatteries.clear();
		mBatteries.addAll(Battery.objects(getApplicationContext()).filter(filter).toList());
				
		//update aircraft spinner list
		mSelectedAircraft = null;
		int aindex = mAircraftSpinner.getSelectedItemPosition();
		if (aindex == Spinner.INVALID_POSITION)
			aindex = 0;
		
		mAircraftSpinnerAdapter.clear();
		mAircraftSpinnerAdapter.add(mEmptyAircraft);
		for (Aircraft a : mAircrafts)
		{
			mAircraftSpinnerAdapter.add(a);
		}
		mAircraftSpinnerAdapter.notifyDataSetChanged();
		Log.d(LOG_TAG, "Updating aircraft spinner: old index = " + aindex + ", adapter count = " + mAircraftSpinnerAdapter.getCount());
		if (aindex >= mAircraftSpinnerAdapter.getCount())
		{
			Log.d(LOG_TAG, "Old aircraft spinner selection no longer valid, reseting to 0");
			aindex = 0;
		}
		mAircraftSpinner.setSelection(aindex);
		if (aindex != 0)
		{
			mSelectedAircraft = (Aircraft) mAircraftSpinner.getSelectedItem();
			Log.d(LOG_TAG, "Old aircraft spinner selection still valid: pos = " + aindex + ", aircraft = " + mSelectedAircraft.getName());
		}
		
		//invalidate selection data if required
		//1- aircraft
		if (mAircraftSpinner.getSelectedItemPosition() == 0 || mAircraftSpinner.getSelectedItemPosition() == Spinner.INVALID_POSITION)
		{
			mSelectedAircraft = null;
		}
		else
		{
			mSelectedAircraft = (Aircraft) mAircraftSpinner.getSelectedItem();
		}
		
		//Update Battery list and selection
		mBatteryListAdapter.notifyDataSetChanged();
				
		boolean stale_data = false;
		for (Map.Entry<Integer, Battery> entry : mSelectedBatteries.entrySet()) {
		    int bindex = entry.getKey();
		    Battery batt = entry.getValue();
		    if (bindex >= mBatteries.size())
		    {
		    	stale_data = true;
		    	break;
		    }
		    Battery batt2 =  mBatteries.get(bindex);
		    if (batt.getId() != batt2.getId())
		    {
		    	stale_data = true;
		    	break;
		    }
		    else
		    {
		    	entry.setValue(batt2); //refresh just in case label has changed
		    }
		}
		
		if (stale_data)
		{
			mSelectedBatteries.clear();
		}
		
		dbloaded = true;
	}
	
    @Override
    public void onBackPressed() {
    	Log.i(LOG_TAG, "onBackPressed intercepted");
    	if (mTimerState == TimerState.OFF)
    	{
    		Log.i(LOG_TAG, "Timer not running, passing key press to parent");
    		super.onBackPressed();
    		return;
    	}
    	
    	//if timer is running, we do not want to kill the activity
    	Log.i(LOG_TAG, "Timer is running, dropping back to home screen isntead");
    	Intent setIntent = new Intent(Intent.ACTION_MAIN);
    	setIntent.addCategory(Intent.CATEGORY_HOME);
    	setIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    	startActivity(setIntent);
    }
	
	//passing the WheelViews in as we intend to reuse this in logging dialog as well    
	public void setTimePickerValue(int seconds, WheelView minPicker, WheelView secPicker)
	{
		int min = seconds/60;
		int sec = seconds%60;
		minPicker.setCurrentItem(min);
		secPicker.setCurrentItem(sec);
	}
	
	public int getTimePickerValue(WheelView minPicker, WheelView secPicker)
	{
		int min = minPicker.getCurrentItem();
		int sec = secPicker.getCurrentItem();
		return (min*60) + sec;
	}

	private void timerAlert(AlertType type)
	{
		Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
		final long[] pattern1 = {300,700,300,700,530,700};
		final long[] pattern2 = {300,700,300,700,300,700,300,700,300,700,300,700};
		
		switch (type)
		{
		case TICK:
			if (mVibrate.isChecked())
				v.vibrate(700);
			if (mBeep.isChecked())
			{
				mBeepCounter += 1;
				mBeepPlayer.start();
			}
			break;
		case SOON:
			if (mVibrate.isChecked())
				v.vibrate(pattern1, -1);
			if (mBeep.isChecked())
			{
				mBeepCounter += 3;
				mBeepPlayer.start();
			}
			break;
		case END:
			if (mVibrate.isChecked())
				v.vibrate(pattern2, -1);
			if (mBeep.isChecked())
			{
				mBeepCounter += 6;
				mBeepPlayer.start();
			}
			break;
		default:
			//nothing happens
		}
	}

	void updateUI()
	{
		//build and update battery selection string
		String bnames = "";
		boolean first = true;
		for (Battery b : mSelectedBatteries.values())
		{
			if (!first)
				bnames += getString(R.string.flighttimer_battery_separator);
			else
				first = false;
			bnames += b.getTag();
		}
		mBatterySelector.setText(bnames);
		
		if (mTimerState != TimerState.OFF)
		{
			mTimePickerMins.setKeepScreenOn(true);
			mStartStop.setText("Stop");
			mAircraftSpinner.setEnabled(false);
			mBatterySelector.setEnabled(false);
			mBeep.setEnabled(false);
			mVibrate.setEnabled(false);
			mUptimer.setEnabled(false);
			mPause.setEnabled(true);
			mLog.setEnabled(false);
			mDirection.setImageResource((mTimerState == TimerState.COUNTING_DOWN || mTimerState == TimerState.COUNTING_DOWN_PAUSED) ? R.drawable.indicator_green_down : R.drawable.indicator_red_up);
		}
		else
		{
			mTimePickerMins.setKeepScreenOn(false);
			mStartStop.setText("Start");
			mAircraftSpinner.setEnabled(true);
			mBatterySelector.setEnabled(true);
			mBeep.setEnabled(true);
			mVibrate.setEnabled(true);
			mUptimer.setEnabled(true);
			mPause.setEnabled(false);
			mLog.setEnabled(mSelectedAircraft != null || !mSelectedBatteries.isEmpty());
			mDirection.setImageResource(R.drawable.indicator_blank);
		}
	
		if (mTimerState == TimerState.COUNTING_DOWN_PAUSED || mTimerState == TimerState.COUNTING_UP_PAUSED)
			mPause.setText("Unpause");
		else
			mPause.setText("Pause");
	}
	
	String buildTimerPrefKey()
	{

		//build a timer pref key of the form: TimerVal_{aircraft id}_{batt1 id}_{batt2 id}
		String pref_key = PREF_TIMER_PREFIX + "_";
		if (mSelectedAircraft != null)
			pref_key += mSelectedAircraft.getId();
		pref_key += "_";
		for (Battery b : mSelectedBatteries.values())
		{
			pref_key += b.getId();
			pref_key += "_";
		}
		
		//add some more _ to ensure backward compatibility with previous version
		int n = 2- mSelectedBatteries.size();
		for (int i = 0; i < n; i++)
		{
			pref_key += "_";
		}
		return pref_key;
	}
	
	void loadTimerPrefValue()
	{
		setTimePickerValue(mPrefs.getInt(buildTimerPrefKey(), DEFAULT_TIMER_SECS), mTimePickerMins, mTimePickerSecs);
	}
	
	//Adapter for WheelView
    private class TimeNumericAdapter extends NumericWheelAdapter {
        //constructor
        public TimeNumericAdapter(Context context, int minValue, int maxValue, String format) {
            super(context, minValue, maxValue, format);
        }
        
        @Override
        protected void configureTextView(TextView view) {
            super.configureTextView(view);
            //view.setTextColor(0xFF00FF00);
            //view.setShadowLayer(20.0f, 0.0f, 0.0f, 0xFFFFFFFF);
        }
        
        @Override
        public View getItem(int index, View cachedView, ViewGroup parent) {
            return super.getItem(index, cachedView, parent);
        }
    }
    
    //expected to be called by service once per second
    public TimerState timerTick()
    {
    	Log.i(LOG_TAG, "Tick received, mTimerState = " + mTimerState);
    	
    	if (mTimerState == TimerState.COUNTING_DOWN)
    	{
    		if (mTimerInitCounter > 0)
    		{
    			mTimerInitCounter--;
    			if (mTimerInitCounter == 0)
    			{
    				timerAlert(AlertType.TICK);
    			}
    		}
    		else
    		{
    			if (mTimerMainCounter > 0)
    				mTimerMainCounter--;
    			
    			setTimePickerValue(mTimerMainCounter, mTimePickerMins, mTimePickerSecs); //update timer display
    			
    			if (mTimerMainCounter == 0)
    			{
    				//done
    				timerAlert(AlertType.END);
    				if (mUptimer.isChecked())
    				{
    					mTimerState = TimerState.COUNTING_UP;
    					updateUI();
    				}
    				else
    				{
    					mTimerState = TimerState.OFF;
    					mFlightLogTime = mTimerCountDownInterval;
    					
    					//if no asset is selected for logging, flight log cannot be done
    					if (mSelectedAircraft != null || !mSelectedBatteries.isEmpty())
    						showDialog(DIALOG_ID_AUTOLOG);
    					
    					updateUI();
    					setTimePickerValue(mTimerCountDownInterval, mTimePickerMins, mTimePickerSecs); //reset timer display
    				}
    			}
    			else if (mTimerMainCounter == 60)
        		{
        			timerAlert(AlertType.SOON);
        		}
    			else if (mTimerMainCounter%60 == 0)
        		{
        			timerAlert(AlertType.TICK);
        		}
			}
    	}
    	else if (mTimerState == TimerState.COUNTING_UP)
    	{
    		mTimerMainCounter++;
    		setTimePickerValue(mTimerMainCounter, mTimePickerMins, mTimePickerSecs); //update timer display
    		if (mTimerMainCounter%60 == 0)
    		{
    			timerAlert(AlertType.TICK);
    		}
    	}
    	
		return mTimerState;
    }
        
    //service connection stuff
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mTimerService = ((LocalTimerService.LocalBinder)service).getService();
            mTimerService.setActivity(FlightTimerActivity.this);
            Log.i(LOG_TAG, "Timer service bounded");
        }

        public void onServiceDisconnected(ComponentName className) {
        	mTimerService = null;
        	Log.i(LOG_TAG, "Timer service unbounded");
        }
    };
}
